package com.songlanyun.modules.order.controller.admin;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import com.songlanyun.common.enums.OrderConstant;
import com.songlanyun.common.enums.RightsOrderConstant;
import com.songlanyun.common.utils.PageUtils;
import com.songlanyun.common.utils.R;
import com.songlanyun.common.utils.custom.MathUtils;
import com.songlanyun.modules.account.service.AccountService;
import com.songlanyun.modules.order.model.vo.SaleStatisticsVo;
import com.songlanyun.modules.order.model.vo.YearOnYearOrderAmountVo;
import com.songlanyun.modules.order.model.vo.YearOnYearOrderNumVo;
import com.songlanyun.modules.order.service.OrderService;
import com.songlanyun.modules.order.service.OrderStatisticsService;
import com.songlanyun.modules.rights.service.RightsOrderService;
import com.songlanyun.modules.saleconfig.entity.SaleConfig;
import com.songlanyun.modules.user.controller.AbstractController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

@RestController
@RequestMapping("admin/order")
public class OrderStatisticsController extends AbstractController {

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private OrderStatisticsService orderStatisticsService;
    @Autowired
    private RightsOrderService rightsOrderService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private OrderService orderService;

    /**
     * 简单统计
     * @return
     */
    @RequestMapping("/statistics/single")
    public R singleStatistics() {
        Long shopId = getShopId(false);
        Map<String,Object> map = new HashMap<>();
        DateTime now = new DateTime();

        CompletableFuture<Map<String, Object>> todayOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer todayOrderNum = orderStatisticsService.loadOrderNumByTime(shopId,
                    DateUtil.beginOfDay(now).getTime(),
                    DateUtil.endOfDay(now).getTime());
            map.put("todayOrderNum", todayOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> todayOrderAmountFuture = CompletableFuture.supplyAsync(() -> {
            BigDecimal todayOrderAmount = orderStatisticsService.loadOrderAmountByTime(shopId,
                    DateUtil.beginOfDay(now).getTime(),
                    DateUtil.endOfDay(now).getTime());
            map.put("todayOrderAmount", todayOrderAmount);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> yesterdayOrderAmountFuture = CompletableFuture.supplyAsync(() -> {
            BigDecimal yesterdayOrderAmount = orderStatisticsService.loadOrderAmountByTime(shopId,
                    DateUtil.beginOfDay(DateUtil.yesterday()).getTime(),
                    DateUtil.endOfDay(DateUtil.yesterday()).getTime());
            map.put("yesterdayOrderAmount", yesterdayOrderAmount);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> sevenDayOrderAmountFuture = CompletableFuture.supplyAsync(() -> {
            BigDecimal sevenDayOrderAmount = orderStatisticsService.loadOrderAmountByTime(shopId,
                    DateUtil.offsetDay(now, -7).getTime(),
                    now.getTime());
            map.put("sevenDayOrderAmount", sevenDayOrderAmount);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> waitPayOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer waitPayOrderNum = orderStatisticsService.loadWaitOperationNum(shopId, OrderConstant.TransactionStatus.WAIT_PAYMENT);
            map.put("waitPayOrderNum", waitPayOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> waitShipOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer waitShipOrderNum = orderStatisticsService.loadWaitOperationNum(shopId, OrderConstant.TransactionStatus.WAIT_SHIP);
            map.put("waitShipOrderNum", waitShipOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> waitReceiptOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer waitReceiptOrderNum = orderStatisticsService.loadWaitOperationNum(shopId, OrderConstant.TransactionStatus.WAIT_RECEIPT);
            map.put("waitReceiptOrderNum", waitReceiptOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> completeOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer completeOrderNum = orderStatisticsService.loadWaitOperationNum(shopId, OrderConstant.TransactionStatus.COMPLETE);
            map.put("completeOrderNum", completeOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> waitDisposeRightsOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer waitDisposeRightsOrderNum = rightsOrderService.loadRightsOrderNum(shopId, 0);
            map.put("waitDisposeRightsOrderNum", waitDisposeRightsOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> waitReceiptRightsOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer waitReceiptRightsOrderNum = rightsOrderService.loadRightsOrderNum(shopId, 1);
            map.put("waitReceiptRightsOrderNum", waitReceiptRightsOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> waitRefundRightsOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer waitRefundRightsOrderNum = rightsOrderService.loadRightsOrderNum(shopId, 2);
            map.put("waitRefundRightsOrderNum", waitRefundRightsOrderNum);
            return map;
        }, threadPoolExecutor);

        CompletableFuture<Map<String, Object>> refundSuccessRightsOrderNumFuture = CompletableFuture.supplyAsync(() -> {
            Integer refundSuccessRightsOrderNum = rightsOrderService.loadRightsOrderNum(shopId, 3);
            map.put("refundSuccessRightsOrderNum", refundSuccessRightsOrderNum);
            return map;
        }, threadPoolExecutor);

        try {
            CompletableFuture.allOf(
                    todayOrderNumFuture,
                    todayOrderAmountFuture,
                    yesterdayOrderAmountFuture,
                    sevenDayOrderAmountFuture,
                    waitPayOrderNumFuture,
                    waitShipOrderNumFuture,
                    waitReceiptOrderNumFuture,
                    completeOrderNumFuture,
                    waitDisposeRightsOrderNumFuture,
                    waitReceiptRightsOrderNumFuture,
                    waitRefundRightsOrderNumFuture,
                    refundSuccessRightsOrderNumFuture

            ).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        return R.ok().put("data", map);
    }

    /**
     * 根据时间（天）统计订单
     * @param params
     * @return
     */
    @RequestMapping("/statistics/buy-days")
    public R statisticsOrderNumByDays(@RequestParam Map<String, Object> params) {
        Long shopId = getShopId(false);
        List<SaleStatisticsVo> list = new ArrayList<>();
        String type = MapUtil.getStr(params, "type");
        String dataType = MapUtil.getStr(params, "data_type");

        List<CompletableFuture<SaleStatisticsVo>> futures = new ArrayList<>();
        CompletableFuture[] futureArr = {};
        switch (type)
        {
            case "week":
                loadWeekStatisticsOrder(shopId,futures,list,dataType);
                break;
            case "month":
                loadMonthStatisticsOrder(shopId,futures,list,dataType);
                break;
            case "custom":
                String startDayStr = MapUtil.getStr(params, "startDay");
                DateTime startDay = DateUtil.parse(startDayStr, "yyyy-MM-dd");
                String endDayStr = MapUtil.getStr(params, "endDay");
                DateTime endDay = DateUtil.parse(endDayStr, "yyyy-MM-dd");
                loadCustomStatisticsOrder(shopId,futures,list,startDay,endDay,dataType);
                break;

        }

        try {
            CompletableFuture.allOf(futures.toArray(futureArr)).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        return R.ok().put("list", list);
    }


    /**
     * 统计订单数与同比值
     * @param params
     * @return
     */
    @RequestMapping("/statistics-order-num/year-on-year")
    public R orderNumYearOnYear(@RequestParam Map<String, Object> params) {
        Long shopId = getShopId(false);
        String type = MapUtil.getStr(params, "type");
        YearOnYearOrderNumVo vo = new YearOnYearOrderNumVo();
        switch (type) {
            case "week":
                vo.setType("week");
                CompletableFuture<YearOnYearOrderNumVo> currentFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime weekStartTime = DateUtil.beginOfWeek(nowDateTime);
                    DateTime weekEndTime = DateUtil.endOfWeek(nowDateTime);
                    Integer num = orderStatisticsService.loadOrderNumByTime(shopId,
                            weekStartTime.getTime(),
                            weekEndTime.getTime());
                    vo.setCurrent(num);
                    return vo;
                }, threadPoolExecutor);

                CompletableFuture<YearOnYearOrderNumVo> prevFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime prevDateTime = DateUtil.offsetMonth(nowDateTime, -12);
                    DateTime prevWeekStartTime = DateUtil.beginOfWeek(prevDateTime);
                    DateTime prevWeekEndTime = DateUtil.endOfWeek(prevDateTime);
                    Integer num = orderStatisticsService.loadOrderNumByTime(shopId,
                            prevWeekStartTime.getTime(),
                            prevWeekEndTime.getTime());
                    vo.setPrev(num);
                    return vo;
                }, threadPoolExecutor);

                try {
                    CompletableFuture.allOf(currentFuture,prevFuture).get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }

                break;
            case "month":
                vo.setType("month");
                CompletableFuture<YearOnYearOrderNumVo> currentMonthFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime monthStartTime = DateUtil.beginOfMonth(nowDateTime);
                    DateTime monthEndTime = DateUtil.endOfMonth(nowDateTime);
                    Integer num = orderStatisticsService.loadOrderNumByTime(shopId,
                            monthStartTime.getTime(),
                            monthEndTime.getTime());
                    vo.setCurrent(num);
                    return vo;
                }, threadPoolExecutor);

                CompletableFuture<YearOnYearOrderNumVo> prevMonthFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime prevMonthDateTime = DateUtil.offsetMonth(nowDateTime, -12);
                    DateTime prevMonthStartTime = DateUtil.beginOfMonth(prevMonthDateTime);
                    DateTime prevMonthEndTime = DateUtil.endOfMonth(prevMonthDateTime);
                    Integer num = orderStatisticsService.loadOrderNumByTime(shopId,
                            prevMonthStartTime.getTime(),
                            prevMonthEndTime.getTime());
                    vo.setPrev(num);
                    return vo;
                }, threadPoolExecutor);

                try {
                    CompletableFuture.allOf(currentMonthFuture,prevMonthFuture).get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                break;
        }

        return R.ok().put("data", vo);
    }



    /**
     * 统计订单销售额与同比值
     * @param params
     * @return
     */
    @RequestMapping("/statistics-order-amount/year-on-year")
    public R orderAmountYearOnYear(@RequestParam Map<String, Object> params) {
        Long shopId = getShopId(false);
        String type = MapUtil.getStr(params, "type");
        YearOnYearOrderAmountVo vo = new YearOnYearOrderAmountVo();
        switch (type) {
            case "week":
                vo.setType("week");
                CompletableFuture<YearOnYearOrderAmountVo> currentFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime weekStartTime = DateUtil.beginOfWeek(nowDateTime);
                    DateTime weekEndTime = DateUtil.endOfWeek(nowDateTime);
                    BigDecimal current = orderStatisticsService.loadOrderAmountByTime(shopId,
                            weekStartTime.getTime(),
                            weekEndTime.getTime());
                    vo.setCurrent(current);
                    return vo;
                }, threadPoolExecutor);

                CompletableFuture<YearOnYearOrderAmountVo> prevFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime prevDateTime = DateUtil.offsetMonth(nowDateTime, -12);
                    DateTime prevWeekStartTime = DateUtil.beginOfWeek(prevDateTime);
                    DateTime prevWeekEndTime = DateUtil.endOfWeek(prevDateTime);
                    BigDecimal prev = orderStatisticsService.loadOrderAmountByTime(shopId,
                            prevWeekStartTime.getTime(),
                            prevWeekEndTime.getTime());
                    vo.setPrev(prev);
                    return vo;
                }, threadPoolExecutor);

                try {
                    CompletableFuture.allOf(currentFuture,prevFuture).get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }

                break;
            case "month":
                vo.setType("month");
                CompletableFuture<YearOnYearOrderAmountVo> currentMonthFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime monthStartTime = DateUtil.beginOfMonth(nowDateTime);
                    DateTime monthEndTime = DateUtil.endOfMonth(nowDateTime);
                    BigDecimal current = orderStatisticsService.loadOrderAmountByTime(shopId,
                            monthStartTime.getTime(),
                            monthEndTime.getTime());
                    vo.setCurrent(current);
                    return vo;
                }, threadPoolExecutor);

                CompletableFuture<YearOnYearOrderAmountVo> prevMonthFuture = CompletableFuture.supplyAsync(() -> {
                    DateTime nowTime = new DateTime();
                    Date nowDateTime = nowTime.toJdkDate();
                    DateTime prevMonthDateTime = DateUtil.offsetMonth(nowDateTime, -12);
                    DateTime prevMonthStartTime = DateUtil.beginOfMonth(prevMonthDateTime);
                    DateTime prevMonthEndTime = DateUtil.endOfMonth(prevMonthDateTime);
                    BigDecimal prev = orderStatisticsService.loadOrderAmountByTime(shopId,
                            prevMonthStartTime.getTime(),
                            prevMonthEndTime.getTime());
                    vo.setPrev(prev);
                    return vo;
                }, threadPoolExecutor);

                try {
                    CompletableFuture.allOf(currentMonthFuture,prevMonthFuture).get();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
                break;
        }

        return R.ok().put("data", vo);
    }



    private void loadCustomStatisticsOrder(Long shopId, List<CompletableFuture<SaleStatisticsVo>> futures, List<SaleStatisticsVo> list, DateTime startDay, DateTime endDay,String dataType) {
        int i = 0;
        while (true)
        {
            DateTime dateTime = DateUtil.offsetDay(startDay, i);
            if(dateTime.isAfterOrEquals(endDay))
            {
                break;
            }
            list.add(new SaleStatisticsVo(){{
                setDateTime(dateTime);
            }});
            i++;
        }

        processOrderStatistics(shopId,futures,list,dataType);
    }


    private void processOrderStatistics(Long shopId,List<CompletableFuture<SaleStatisticsVo>> futures,List<SaleStatisticsVo> list,String dataType)
    {
        if(dataType.equals("count"))
        {
            processOrderNumFuture(shopId,futures,list);
        }
        else
        {
            processOrderAmountFuture(shopId,futures,list);
        }
    }

    private void loadWeekStatisticsOrder(Long shopId,List<CompletableFuture<SaleStatisticsVo>> futures,List<SaleStatisticsVo> list,String dataType)
    {
        DateTime now = new DateTime();
        DateTime startDate = DateUtil.beginOfWeek(now.toJdkDate());
        for(int i = 0;i<7;i++)
        {
            DateTime dateTime = DateUtil.offsetDay(startDate, i);
            list.add(new SaleStatisticsVo(){{
                setDateTime(dateTime);
            }});
        }

        processOrderStatistics(shopId,futures,list,dataType);
    }

    private void loadMonthStatisticsOrder(Long shopId,List<CompletableFuture<SaleStatisticsVo>> futures,List<SaleStatisticsVo> list,String dataType)
    {
        DateTime now = new DateTime();
        DateTime startDate = DateUtil.beginOfMonth(now.toJdkDate());
        DateTime endDate = DateUtil.endOfMonth(now.toJdkDate());

        int i = 0;
        while (true)
        {
            DateTime dateTime = DateUtil.offsetDay(startDate, i);
            if(dateTime.isAfterOrEquals(endDate))
            {
                break;
            }
            list.add(new SaleStatisticsVo(){{
                setDateTime(dateTime);
            }});
            i++;

        }

        processOrderStatistics(shopId,futures,list,dataType);
    }


    private void processOrderNumFuture(Long shopId,List<CompletableFuture<SaleStatisticsVo>> futures,List<SaleStatisticsVo> list)
    {
        for (SaleStatisticsVo vo:list) {
            CompletableFuture<SaleStatisticsVo> future = CompletableFuture.supplyAsync(() -> {
                Integer num = orderStatisticsService.loadOrderNumByTime(shopId,
                        DateUtil.beginOfDay(vo.getDateTime().toJdkDate()).getTime(),
                        DateUtil.endOfDay(vo.getDateTime().toJdkDate()).getTime());
                vo.setOrderNum(num);
                return vo;
            }, threadPoolExecutor);
            futures.add(future);
        }
    }

    private void processOrderAmountFuture(Long shopId,List<CompletableFuture<SaleStatisticsVo>> futures,List<SaleStatisticsVo> list)
    {
        for (SaleStatisticsVo vo:list) {
            CompletableFuture<SaleStatisticsVo> future = CompletableFuture.supplyAsync(() -> {
                BigDecimal amount = orderStatisticsService.loadOrderAmountByTime(shopId,
                        DateUtil.beginOfDay(vo.getDateTime().toJdkDate()).getTime(),
                        DateUtil.endOfDay(vo.getDateTime().toJdkDate()).getTime());
                vo.setOrderAmount(amount);
                return vo;
            }, threadPoolExecutor);
            futures.add(future);
        }
    }


    /**
     * 权益统计
     * @return
     */
    @GetMapping("equity/{userId}")
    public R loadStatistics(@PathVariable("userId") Long userId) {
        List<Long> userIds = accountService.getDownAllUserIds(userId);
        if(CollectionUtil.isNotEmpty(userIds))
        {
            BigDecimal v = orderService.loadActivatingOrderCount(userIds);
            return R.ok().put("data", v);
        }
        return R.ok().put("data", 0);

    }



}
